import json

from coin.exchange.base.kr_ws.ws_client_base import (WsClientHandlerBase, WsClientPrinter)
from coin.exchange.base.kr_ws.ws_parser_base import (WebsocketParserManagerBase, WsFeedHandlerBase)
import coin.proto.coin_feed_pb2 as proto_pb2


def _strip_left(line, prefix):
  if line.startswith(prefix):
    return line[len(prefix):]
  else:
    return line


def _parse_TradeSide(side):
  if side == "BID":
    return proto_pb2.TRADE_BUY_SIDE
  elif side == "ASK":
    return proto_pb2.TRADE_SELL_SIDE
  else:
    return proto_pb2.TRADE_NO_SIDE


def _parse_orderbookUnits(json_msg):
  bids = []
  asks = []
  for elem in json_msg:
    bids.append(
        proto_pb2.BookEntry(price_float=float(elem["bidPrice"]), qty_float=float(elem["bidSize"])))
    asks.append(
        proto_pb2.BookEntry(price_float=float(elem["askPrice"]), qty_float=float(elem["askSize"])))
  bids = sorted(bids, key=lambda elem: -elem.price_float)
  asks = sorted(asks, key=lambda elem: elem.price_float)
  return proto_pb2.BookMessage(bid=bids, ask=asks)


class UpbitParser(object):
  @staticmethod
  def parse_trade(update, json_msg):
    # Trade {'tradeDate': '2018-01-11', 'tradeTime': '12:33:09', 'tradeTimestamp': 1515673989000, 'tradePrice': 19385000.0, 'tradeVolume': 0.07014343, 'askBid': 'BID', 'prevClosingPrice': 21317000.0, 'change': 'FALL', 'changePrice': 1932000.0, 'sequentialId': 1515673990621000, 'type': 'crixTrade', 'code': 'CRIX.UPBIT.KRW-BTC', 'timestamp': 1515673990621, 'streamType': 'REALTIME'}
    # Trade {'tradeDate': '2018-01-11', 'tradeTime': '12:32:44', 'tradeTimestamp': 1515673964000, 'tradePrice': 19460000.0, 'tradeVolume': 0.02804423, 'askBid': 'ASK', 'prevClosingPrice': 21317000.0, 'change': 'FALL', 'changePrice': 1857000.0, 'sequentialId': 1515673965839000, 'type': 'crixTrade', 'code': 'CRIX.UPBIT.KRW-BTC', 'timestamp': 1515673965839, 'streamType': 'SNAPSHOT'}
    trade = proto_pb2.TradeMessage(price_float=float(json_msg["tradePrice"]),
                                   qty_float=float(json_msg["tradeVolume"]),
                                   side=_parse_TradeSide(json_msg["askBid"]))

    feed = proto_pb2.FeedMessage(feeder_id=0,
                                 machine_id=0,
                                 symbol=json_msg["code"],
                                 exchange_time=int(json_msg["timestamp"]) * 1000000,
                                 fetched_time=int(update.timestamp * 1e+9),
                                 trade=trade)

    return feed

  @staticmethod
  def parse_book(update, json_msg):
    # Book {'totalAskSize': 4.61203152, 'totalBidSize': 3.17162007, 'orderbookUnits': [{'askPrice': 19385000.0, 'bidPrice': 19366000.0, 'askSize': 0.13232163, 'bidSize': 1.26927829}, {'askPrice': 19397000.0, 'bidPrice': 19360000.0, 'askSize': 0.12612479, 'bidSize': 0.0044293}, {'askPrice': 19399000.0, 'bidPrice': 19358000.0, 'askSize': 0.10774815, 'bidSize': 0.02939148}, {'askPrice': 19400000.0, 'bidPrice': 19352000.0, 'askSize': 0.40684765, 'bidSize': 0.08982062}, {'askPrice': 19420000.0, 'bidPrice': 19350000.0, 'askSize': 1.03465971, 'bidSize': 1.31688569}, {'askPrice': 19425000.0, 'bidPrice': 19345000.0, 'askSize': 0.01149053, 'bidSize': 0.38553166}, {'askPrice': 19430000.0, 'bidPrice': 19333000.0, 'askSize': 0.32247407, 'bidSize': 0.00010862}, {'askPrice': 19439000.0, 'bidPrice': 19330000.0, 'askSize': 0.53853404, 'bidSize': 0.00021726}, {'askPrice': 19440000.0, 'bidPrice': 19327000.0, 'askSize': 1.87810388, 'bidSize': 0.05310144}, {'askPrice': 19441000.0, 'bidPrice': 19320000.0, 'askSize': 0.05372707, 'bidSize': 0.02285571}], 'type': 'crixOrderbook', 'code': 'CRIX.UPBIT.KRW-BTC', 'timestamp': 1515673992031, 'streamType': 'REALTIME'}
    # Book {'totalAskSize': 4.99332862, 'totalBidSize': 2.73889364, 'orderbookUnits': [{'askPrice': 19461000.0, 'bidPrice': 19450000.0, 'askSize': 0.06322096, 'bidSize': 0.72707455}, {'askPrice': 19467000.0, 'bidPrice': 19445000.0, 'askSize': 0.07803143, 'bidSize': 0.54232646}, {'askPrice': 19468000.0, 'bidPrice': 19440000.0, 'askSize': 0.98068478, 'bidSize': 0.01952895}, {'askPrice': 19470000.0, 'bidPrice': 19430000.0, 'askSize': 0.00022011, 'bidSize': 0.083}, {'askPrice': 19471000.0, 'bidPrice': 19420000.0, 'askSize': 0.17073521, 'bidSize': 0.57835672}, {'askPrice': 19474000.0, 'bidPrice': 19419000.0, 'askSize': 1.72376981, 'bidSize': 0.01217208}, {'askPrice': 19499000.0, 'bidPrice': 19417000.0, 'askSize': 0.09087196, 'bidSize': 0.00010815}, {'askPrice': 19500000.0, 'bidPrice': 19413000.0, 'askSize': 1.07619687, 'bidSize': 0.08716957}, {'askPrice': 19501000.0, 'bidPrice': 19404000.0, 'askSize': 0.37626128, 'bidSize': 0.06045822}, {'askPrice': 19503000.0, 'bidPrice': 19402000.0, 'askSize': 0.43333621, 'bidSize': 0.62869894}], 'type': 'crixOrderbook', 'code': 'CRIX.UPBIT.KRW-BTC', 'timestamp': 1515673966837, 'streamType': 'SNAPSHOT'}
    book = _parse_orderbookUnits(json_msg["orderbookUnits"])

    feed = proto_pb2.FeedMessage(feeder_id=0,
                                 machine_id=0,
                                 symbol=json_msg["code"],
                                 exchange_time=int(json_msg["timestamp"]) * 1000000,
                                 fetched_time=int(update.timestamp * 1e+9),
                                 book=book)

    return feed


class UpbitParserManager(WebsocketParserManagerBase):
  def __init__(self, handler):
    super().__init__(handler=handler)

  def on_payload_elem(self, update, payload_elem):
    type = payload_elem["type"]
    streamType = payload_elem["streamType"]
    if streamType == "REALTIME" and type == "crixTrade":
      pb = UpbitParser.parse_trade(update, payload_elem)
      self._handler.on_trade(pb)
    elif streamType == "REALTIME" and type == "crixOrderbook":
      pb = UpbitParser.parse_book(update, payload_elem)
      self._handler.on_book(pb)
    else:
      pass

  def on_recv_msg(self, update):
    payload_str = update.msg
    if True:
      if payload_str.startswith("a"):
        # a?
        payload_str = _strip_left(payload_str, "a")
        payload = json.loads(payload_str)
        for payload_elem_str in payload:
          payload_elem = json.loads(payload_elem_str)
          self.on_payload_elem(update, payload_elem)
      elif payload_str.startswith("h"):
        # heartbeat
        pass
      elif payload_str.startswith("o"):
        # open
        pass
      else:
        pass
